﻿using System;

namespace Quail.Silverlight
{
    /// <summary>
    /// A standard silverlight control
    /// </summary>
    public class SlvControl : SlvControlContainer
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="SlvControl"/> class.
        /// </summary>
        public SlvControl()
            : base(null)
        {
            ParentControlChanged += OnParentControlChanged;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SlvControl"/> class.
        /// </summary>
        /// <param name="container">The container for the control.</param>
        /// <param name="selector">The selector.</param>
        public SlvControl(SlvControlContainer container, string selector)
            : base(container)
        {
            MySelector = selector;
            SetPage();
        }

    	/// <summary>
        /// Initializes a new instance of the <see cref="SlvControl"/> class.
        /// </summary>
        /// <param name="control">The control.</param>
        public SlvControl(SlvControl control)
            : base(control.ParentControl)
        {
            MySelector = control.MySelector;
    	    SetPage();
        }

        /// <summary>
        /// Gets the Page that contains this control
        /// </summary>
        public override WebPage Page
        {
            get
            {
                return SlvPage.Page;
            }
        }

        /// <summary>
        /// Gets the Silverlight page.
        /// </summary>
        public SlvPage SlvPage
        {
            get; private set;
        }

        /// <summary>
        /// Gets or sets my selector
        /// </summary>
        public string MySelector
        {
            get;
            set;
        }

        /// <summary>
        /// Gets this control's jQuery selector, including any parent selectors
        /// </summary>
        public override string Selector
        {
            get
            {
                string mysel;
                if (!string.IsNullOrEmpty(MySelector))
                    mysel = MySelector;
                else
                    throw new NotSupportedException("Invalid Selector specified");

                if (string.IsNullOrEmpty(mysel))
                    throw new NotSupportedException("Invalid Selector specified - Selector is blank");

                if (ParentControl is SlvControl)
                    return (ParentControl as SlvControl).Selector + " " + mysel;

                return mysel;
            }

            set
            {
                throw new NotSupportedException("Unable to set a SlvControl selector");
            }

        }

        /// <summary>
        /// Gets the ActiveTask for the test
        /// </summary>
        public override Task ActiveTask
        {
            get { return Page.ActiveTask; }
        }

        /// <summary>
        /// Gets the current WebBrowser instance
        /// </summary>
        public override WebBrowser Browser
        {
            get { return Page.Browser; }
        }

        /// <summary>
        /// Gets a value indicating whether the control is Visible.
        /// </summary>
        public override bool Visible
        {
            get
            {
                if (!Exists)
                    return false;

                return GetPropertyValue("Visible") == "true";
            }
        }

        /// <summary>
        /// Gets a value indicating whether this <see cref="SlvPage"/> is enabled.
        /// </summary>
        /// <value><c>true</c> if enabled; otherwise, <c>false</c>.</value>
        public override bool Enabled
        {
            get
            {
                return Exists && !Disabled;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the control is Disabled.
        /// </summary>
        public bool Disabled
        {
            get
            {
                return GetPropertyValue("Disabled") == "true";
            }
        }

        /// <summary>
        /// Gets a value indicating whether the silverlight control Exists.
        /// </summary>
        public override bool Exists
        {
            get
            {
                if (!SlvPage.Exists)
                    return false;

                var task = BeginTask("Exists");
                try
                {
                    var eval = SlvPage.PageSelector + "." + ObjectSelector + "!=null";
                    var str = SlvPage.GetEval(eval);
                    task.AddAttribute("Result", str);
                    task.Rollback();
                    return str == "true";
                }
                catch (Exception e)
                {
                    task.Finish(e);
                    throw;
                }
            }
        }

        /// <summary>
        /// Gets the Silvernium selector for the object itself
        /// </summary>
        protected string ObjectSelector
        {
            get
            {
                return "FindControl('" + Selector.Replace("'", "\\'") + "')";
            }
        }

        /// <summary>
        /// Begins a task for logging
        /// </summary>
        /// <param name="taskname">The name of the task</param>
        /// <returns>A Task object</returns>
        public Task BeginTask(string taskname)
        {
            return BeginTask(taskname, null);
        }

        /// <summary>
        /// The begin task.
        /// </summary>
        /// <param name="taskname">The taskname.</param>
        /// <param name="message">The message.</param>
        /// <returns>A valid Task object</returns>
        public Task BeginTask(string taskname, string message)
        {
            var task = ActiveTask.BeginTask(taskname);
            task.AddAttribute("Type", GetType().ToString());
            task.AddAttribute("Selector", Selector);
            if (!string.IsNullOrEmpty(message))
                task.AddAttribute("Details", message);

            return task;
        }

        /// <summary>
        /// Executes a method on the silverlight control
        /// </summary>
        /// <param name="method">name of method to execute</param>
        /// <param name="methodparms">parameters for the method</param>
        /// <returns>the return value of the method</returns>
        public string CallMethod(string method, params string[] methodparms)
        {
            return SlvPage.CallMethod(BeginTask("CallMethod"), GetObjectSelector(method), methodparms);
        }

        /// <summary>
        /// Gets a property value from the control
        /// </summary>
        /// <param name="propertyName">name of the property</param>
        /// <returns>The property value</returns>
        public string GetPropertyValue(string propertyName)
        {
            return SlvPage.GetPropertyValue(BeginTask("GetPropertyValue"), GetObjectSelector(propertyName));
        }

        /// <summary>
        /// Gets a property value from an array from the control
        /// </summary>
        /// <param name="propertyName">name of the property</param>
        /// <returns>The property value</returns>
        public string[] GetPropertyValueArray(string propertyName)
        {
            return SlvPage.GetPropertyValueArray(BeginTask("GetPropertyValueArray"), GetObjectSelector(propertyName));
        }

        /// <summary>
        /// Sets a property value to a silverlight control
        /// </summary>
        /// <param name="propertyName">the property to set</param>
        /// <param name="value">the new value</param>
        public void SetPropertyValue(string propertyName, string value)
        {
            SlvPage.SetPropertyValue(BeginTask("SetPropertyValue"), GetObjectSelector(propertyName), value);
        }

        /// <summary>
        /// Performs a simple Click call on the control
        /// </summary>
        public void Click()
        {
            CallMethod("DoClick");
        }

        /// <summary>
        /// Waits for the control to become visible
        /// </summary>
        public void Sync()
        {
            var task = BeginTask("Sync");
            try
            {
                var sw = Page.Browser.GetStopwatch();

                while (!Exists && !sw.Expired)
                    Page.Browser.Sleep(100);

                if (!Exists)
                    throw new SlvControlException(this, LogStatus.FailTimeout, "Timed out waiting for control to become available");
                
                task.Finish();
            }
            catch (Exception e)
            {
                task.Finish(e);
                throw;
            }
        }

        /// <summary>
        /// Adds to the Control's selector
        /// </summary>
        /// <param name="thing">What you want to find on the control</param>
        /// <returns>new selector</returns>
        protected string GetObjectSelector(string thing)
        {
            return ObjectSelector + "." + thing;
        }

        private void SetPage()
        {
            if (ParentControl is SlvPage)
                SlvPage = ParentControl as SlvPage;
            else if (ParentControl is SlvControl)
                SlvPage = (ParentControl as SlvControl).SlvPage;
            else
                SlvPage = null;
        }

        private void OnParentControlChanged(object sender, EventArgs e)
        {
            SetPage();
        }

        /// <summary>
        /// Class for exceptions on the SlvControl
        /// </summary>
        public class SlvControlException : LogStatusException
        {
            /// <summary>
            /// Initializes a new instance of the <see cref="SlvControlException"/> class.
            /// </summary>
            /// <param name="control">The control.</param>
            /// <param name="message">The message.</param>
            public SlvControlException(SlvControl control, string message)
                : this(control, LogStatus.Fail, message)
            {
            }

            /// <summary>
            /// Initializes a new instance of the <see cref="SlvControlException"/> class.
            /// </summary>
            /// <param name="control">The control.</param>
            /// <param name="status">The status to log</param>
            /// <param name="message">The message.</param>
            public SlvControlException(SlvControl control, LogStatus status, string message)
                : base(status, message)
            {
                Control = control;
            }

            /// <summary>
            /// Gets the control with the exception
            /// </summary>
            public SlvControl Control
            {
                get;
                private set;
            }
        }
    }
}
